use crate::{mqi, set_mqrfh2_default, set_mqcih_default};

pub type NameValueData = Vec<u8>;

#[derive(Debug, Clone)]
pub enum MQHeader {
    RFH2 {
        mqrfh2: mqi::MQRFH2,
        name_value_data: Vec<NameValueData>,
    },
    CIH {
        mqcih: mqi::MQCIH
    }
}

impl MQHeader {
    pub fn rfh2_builder() -> MQRFH2Builder {
        MQRFH2Builder::new()
    }
    pub fn cih_builder() -> MQCIHBuilder {
        MQCIHBuilder::new()
    }
    pub fn into_byte_vec(&self) -> Vec<u8> {
        use MQHeader::*;
        match self {
            RFH2{ mqrfh2, name_value_data } => {
                info!("{:?}", mqrfh2);
                let rfh2 = unsafe { struct_as_u8_slice(mqrfh2) };
                let mut result = Vec::new();
                result.extend_from_slice(rfh2);
                for name_value_dato in name_value_data {
                    // Tricky part - we assume that QM running on big-endian system is capable of
                    // processing little-endian headers. If this assumption is weong, next line is a bug!
                    //                                                                   V
                    result.extend_from_slice(&(name_value_dato.len() as mqi::MQLONG).to_le_bytes());
                    result.extend_from_slice(&name_value_dato);
                }
                result
            },
            CIH {..} => {
                todo!();
            }
        }
    }
}

unsafe fn struct_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        std::mem::size_of::<T>(),
    )
}

#[derive(Debug)]
pub struct MQRFH2Builder {
    mqrfh2: mqi::MQRFH2,
    name_value_data: Vec<NameValueData>,
}

/// https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.ref.dev.doc/q099320_.htm
/// Documentation on JMS <> MQ  mapping can be found
/// [here]: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q032000_.htm
impl MQRFH2Builder {
    pub fn new() -> MQRFH2Builder {
        let mqrfh2 = unsafe {
            let mut mqrfh2: mqi::MQRFH2 = std::mem::zeroed();
            set_mqrfh2_default(&mut mqrfh2);
            mqrfh2
        };
        MQRFH2Builder {mqrfh2, name_value_data: Vec::new() }
    }
    pub fn build(&mut self) -> MQHeader {
        assert!(!self.name_value_data.is_empty(),
                "MQRFH2 structure is not fully initialized: empty NameValueData");
        MQHeader::RFH2 {
            mqrfh2: self.mqrfh2,
            name_value_data: self.name_value_data.clone(),
        }
    }
    pub fn with_folder(&mut self, data: &str) ->  &mut Self {
        let mut data = data.to_owned();
        let mut data_len = data.as_bytes().len() as i32;
        let remainder = data_len % 4;
        if remainder != 0 {
            for _ in 0..remainder {
                data.push_str(" ");
            }
            data_len += remainder;
        }
        // When this function is called for the first time, StrucLength is equal to
        // ibm_mq_sys::MQRFH_STRUC_LENGTH_FIXED_2
        self.mqrfh2.StrucLength =
            self.mqrfh2.StrucLength
            + data_len
            + std::mem::size_of::<mqi::MQLONG>() as i32; //size of  NameValueLength
        self.name_value_data.push(Vec::from(data.as_bytes()));
        self
    }
}

pub struct MQCIHBuilder {
    mqcih: mqi::MQCIH
}

impl MQCIHBuilder {
    fn new() -> MQCIHBuilder {
        let mqcih = unsafe {
            let mut mqcih: mqi::MQCIH = std::mem::zeroed();
            set_mqcih_default(&mut mqcih);
            mqcih
        };
        MQCIHBuilder{mqcih}
    }
    pub fn build(&self) -> MQHeader {
        MQHeader::CIH { mqcih: self.mqcih }
    }
}
